using AOT;
using SocketWeaver.FixedMath;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace SocketWeaver.FPhysics2D
{
    public class FPhysics2D
    {
        //
        static FPhysicsWorld2D _world;

        //
        internal static SortedList<UInt16, FPhysicsBody2D> _bodySortedList = new SortedList<UInt16, FPhysicsBody2D>();

        //layer
        static Dictionary<int, int> _masksByLayer = new Dictionary<int, int>();

        //rollback
        internal static Action<UInt32, UInt16, IntPtr> _rollbackAddRigidbodyCallback;
        internal static Action<UInt32, UInt16> _rollbackRemoveRigidbodyCallback;

        //collision 
        static Dictionary<UInt64, Collision2DCache> _collisionCache = new Dictionary<ulong, Collision2DCache>();

        //used for overlap queries
        static ColliderId2D[] _queryColliderIDs = new ColliderId2D[FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D];

        [MonoPInvokeCallback(typeof(RollbackAddRigidbodyCallback2D))]
        public static void OnRollbackAddRigidbodyCallback(UInt32 externalID, UInt16 bodyID, IntPtr previousBody, IntPtr world)
        {
            if(_rollbackAddRigidbodyCallback == null)
            {
                return;
            }

            _rollbackAddRigidbodyCallback(externalID, bodyID, previousBody);
        }

        [MonoPInvokeCallback(typeof(RollbackRemoveRigidbodyCallback2D))]
        public static void OnRollbackRemoveRigidbodyCallback(UInt32 externalID, UInt16 bodyID, IntPtr body, IntPtr world)
        {
            if(_rollbackRemoveRigidbodyCallback == null)
            {
                return;
            }

            _rollbackRemoveRigidbodyCallback(externalID, bodyID);
        }

        [MonoPInvokeCallback(typeof(ContactEnterCallBack2D))]
        public static void OnContactEnterCallback(IntPtr contactPtr, UInt64 contactId)
        {
            HandleCollision(contactId, true);
        }

        [MonoPInvokeCallback(typeof(ContactExitCallBack2D))]
        public static void OnContactExitCallBack(IntPtr contactPtr, UInt64 contactId)
        {
            HandleCollision(contactId, false);
        }

        static void HandleCollision(UInt64 contactId, bool enter)
        {
            //Debug.Log($"HANDLE COLLISION {contactId} ENTER={enter}");
            UInt32 bodyIds = (UInt32)(contactId / 1000000);

            UInt16 body1Id = (UInt16)(bodyIds % 100000);
            UInt16 body2Id = (UInt16)(bodyIds / 100000);

            if (_bodySortedList.ContainsKey(body1Id) && _bodySortedList.ContainsKey(body2Id))
            {
                FRigidbody2D rigidbody1 = _bodySortedList[body1Id].rigidBody;
                FRigidbody2D rigidbody2 = _bodySortedList[body2Id].rigidBody;

                if (rigidbody1._collisionEvents || rigidbody2._collisionEvents)
                {

                    UInt32 fixtureIds = (UInt32)(contactId % 1000000);

                    byte fixture1Id = (byte)(fixtureIds % 1000);
                    byte fixture2Id = (byte)(fixtureIds / 1000);

                    if (!_collisionCache.ContainsKey(contactId))
                    {
                        Collision2DCache cache = new Collision2DCache();
                        cache.collider1 = new ColliderId2D { bodyId = body1Id, fixtureId = fixture1Id };
                        cache.collider2 = new ColliderId2D { bodyId = body2Id, fixtureId = fixture2Id };
                        _collisionCache[contactId] = cache;
                    }

                    if (rigidbody1._collisionEvents)
                    {
                        if(enter)
                        {
                            rigidbody1.AddEnterCollision(contactId);
                        }
                        else
                        {
                            rigidbody1.AddExitCollision(contactId);
                        }
                    }

                    if (rigidbody2._collisionEvents)
                    {
                        if (enter)
                        {
                            rigidbody2.AddEnterCollision(contactId);
                        }
                        else
                        {
                            rigidbody2.AddExitCollision(contactId);
                        }
                    }
                }
            }
            else
            {
                Debug.LogError($"Rigibody not found: body1Id={body1Id} body2Id={body2Id}");
            }
        }

        static void ReadCollisionLayerMatrix()
        {
            for (int i = 0; i < 32; i++)
            {
                int mask = 0;
                for (int j = 0; j < 32; j++)
                {
                    if (!Physics2D.GetIgnoreLayerCollision(i, j))
                    {
                        mask |= 1 << j;
                    }
                }
                _masksByLayer.Add(i, mask);
            }
        }

        internal static void Initialize(FVector2 gravity, Action<UInt32, UInt16, IntPtr> rollbackAddRigidbodyCallback, Action<UInt32, UInt16> rollbackRemoveRigidbodyCallback)
        {
            NativeFPPhysics2D.Initialize();

            _rollbackAddRigidbodyCallback = rollbackAddRigidbodyCallback;
            _rollbackRemoveRigidbodyCallback = rollbackRemoveRigidbodyCallback;

            ReadCollisionLayerMatrix();
            _world = CreateWorld(gravity, false, true);
        }

        public static void CleanUp()
        {
            //rigidBodies.Clear();
            _masksByLayer.Clear();
            //bodyDictionary.Clear();
            _bodySortedList.Clear();

            _collisionCache.Clear();
            DestroyWorld(_world);

            _world = null;
        }

        static FPhysicsWorld2D CreateWorld(FVector2 gravity, bool allowSleep, bool warmStart)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.CreateWorld(
                gravity,
                allowSleep,
                warmStart,
                OnContactEnterCallback,
                OnContactExitCallBack,
                OnRollbackAddRigidbodyCallback,
                OnRollbackRemoveRigidbodyCallback);

            return new FPhysicsWorld2D(m_NativeObject);
        }

        static void DestroyWorld(FPhysicsWorld2D world)
        {
            NativeFPPhysics2D.DestroyWorld(world.IntPointer);
        }

        public static void Step(FFloat deltaTime, int velocityIterations)
        {
            NativeFPPhysics2D.Step(_world.IntPointer, deltaTime, velocityIterations, 1);
        }

        public static void ExportFromEngine()
        {
            foreach (var pair in _bodySortedList)
            {
                FPhysicsBody2D body = pair.Value;
                ReadNativeBody(body);
                body.SyncTransform();
            }
        }

        public static void InvokeCollisionEvents()
        {
            foreach (var pair in _bodySortedList)
            {
                FPhysicsBody2D body = pair.Value;
                if(body.rigidBody._collisionEvents)
                {
                    body.rigidBody.InvokeCollisionEvents();
                }
            }
        }

        public static void ResetCollisionEvents()
        {
            foreach (var pair in _bodySortedList)
            {
                FPhysicsBody2D body = pair.Value;
                if (body.rigidBody._collisionEvents)
                {
                    body.rigidBody.ResetCollisionEvents();
                }
            }
        }

        //body

        internal static FPhysicsBody2D AddBody
            (int bodyType,
            FVector2 position,
            FFloat angle,
            FFloat linearDamping,
            FFloat angularDamping,
            bool fixedRotation,
            FFloat gravityScale,
            FRigidbody2D rigidBody2D,
            UInt32 externalID)
        {
            UInt16 bodyID = 0;

            IntPtr m_NativeObject = NativeFPPhysics2D.CreateBody(
                _world.IntPointer,
                bodyType,
                position,
                angle,
                linearDamping,
                angularDamping,
                fixedRotation,
                gravityScale,
                externalID,
                ref bodyID);

            FPhysicsBody2D body2D = new FPhysicsBody2D(m_NativeObject, bodyID, externalID, rigidBody2D);
            _bodySortedList[bodyID] = body2D;

            ReadNativeBody(body2D);

            return body2D;
        }

        static void ReadNativeBody(FPhysicsBody2D body2D)
        {
            NativeFPPhysics2D.GetTransform(body2D.IntPointer, ref body2D.position, ref body2D.angle);
            NativeFPPhysics2D.GetVelocity(body2D.IntPointer, ref body2D.linearVelocity, ref body2D.angularVelocity);
        }

        internal static void ReadBodyMassInfo(FPhysicsBody2D body2D)
        {
            NativeFPPhysics2D.GetBodyMassInfo(body2D.IntPointer, ref body2D.mass);
        }

        internal static void DestoryBody(FPhysicsBody2D body2D, FRigidbody2D rigidBody2D)
        {
            if (_bodySortedList.ContainsKey(body2D.bodyID))
            {
                _bodySortedList.Remove(body2D.bodyID);
            }

            //if the _world is null, probably the FPhysicsManager has been destroyed. not need to destroy body
            if(_world != null)
            {
                NativeFPPhysics2D.DestroyBody(_world.IntPointer, body2D.IntPointer);
            }
        }

        internal static void UpdateBodyTransForm(FPhysicsBody2D body, FVector2 pos, FFloat angle)
        {
            NativeFPPhysics2D.UpdateBodyTransform(body.IntPointer, pos, angle);
        }

        internal static void UpdateBodyVelocity(FPhysicsBody2D body, FVector2 linearVelocity, FFloat angularVelocity)
        {
            NativeFPPhysics2D.UpdateBodyVelocity(body.IntPointer, linearVelocity, angularVelocity);
        }

        internal static void UpdateBodyProperties(FPhysicsBody2D body,
            int bodyType,
            FFloat linearDamping,
            FFloat angularDamping,
            bool fixedRotation,
            FFloat gravityScale)
        {
            NativeFPPhysics2D.UpdateBodyProperties(body.IntPointer, bodyType, linearDamping, angularDamping, fixedRotation, gravityScale);
        }

        internal static void SetEnabled(FPhysicsBody2D body, bool enabled)
        {
            //if the _world is null, probably the FPhysicsManager has been destroyed. not need to destroy body
            if (_world != null)
            {
                NativeFPPhysics2D.SetEnabled(body.IntPointer, enabled);
            }
        }

        //force
        internal static void ApplyForce(FPhysicsBody2D body, FVector2 point, FVector2 force)
        {
            NativeFPPhysics2D.ApplyForce(body.IntPointer, point, force);
        }

        internal static void ApplyForceToCenter(FPhysicsBody2D body, FVector2 force)
        {
            NativeFPPhysics2D.ApplyForceToCenter(body.IntPointer, force);
        }

        internal static void ApplyTorque(FPhysicsBody2D body, FFloat torque)
        {
            NativeFPPhysics2D.ApplyTorque(body.IntPointer, torque);
        }

        internal static void ApplyLinearImpulse(FPhysicsBody2D body, FVector2 point, FVector2 impulse)
        {
            NativeFPPhysics2D.ApplyLinearImpulse(body.IntPointer, point, impulse);
        }

        internal static void ApplyLinearImpulseToCenter(FPhysicsBody2D body, FVector2 impulse)
        {
            NativeFPPhysics2D.ApplyLinearImpulseToCenter(body.IntPointer, impulse);
        }

        internal static void ApplyAngularImpulse(FPhysicsBody2D body, FFloat impulse)
        {
            NativeFPPhysics2D.ApplyAngularImpulse(body.IntPointer, impulse);
        }

        //2D fixture
        internal static FPhysicsFixture2D AddFixture(FPhysicsBody2D body2D, FPhysicsShape2D shape2D, FFloat density, FFloat mass)
        {
            byte fixtureId = 0;
            IntPtr m_NativeObject = NativeFPPhysics2D.AddFixtureToBody(body2D.IntPointer, shape2D.IntPointer, density, mass, ref fixtureId);
            return new FPhysicsFixture2D(m_NativeObject, fixtureId);
        }

        internal static FPhysicsShape2D GetShapeOfFixture(FPhysicsFixture2D fixture2D)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.GetShapeOfFixture(fixture2D.IntPointer);
            return new FPhysicsShape2D(m_NativeObject);
        }

        internal static void SetLayer(FPhysicsFixture2D fixture, int layer, bool refilter)
        {
            int mask = _masksByLayer[layer];
            //shift layer
            int shiftedLayer = 1 << layer;

            NativeFPPhysics2D.SetLayer(fixture.IntPointer, shiftedLayer, mask, refilter);
        }

        internal static void SetFixtureProperties(FPhysicsFixture2D fixture, bool isTrigger, FFloat friction, FFloat bounciness)
        {
            NativeFPPhysics2D.SetFixtureProperties(fixture.IntPointer, isTrigger, friction, bounciness);
        }

        //2D shapes
        public static FPhysicsShape2D CreateCircle(FFloat radius, FVector2 center)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.CreateCircle(radius, center);
            return new FPhysicsShape2D(m_NativeObject);
        }

        public static void UpdateCircle(FPhysicsShape2D shape, FPhysicsFixture2D fixture, FFloat radius, FVector2 center)
        {
            NativeFPPhysics2D.UpdateCircle(shape.IntPointer, fixture.IntPointer, radius, center);
        }

        public static FPhysicsShape2D CreateBox(FFloat width, FFloat height, FVector2 center, FFloat angle)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.CreateBox(width, height, center, angle);
            return new FPhysicsShape2D(m_NativeObject);
        }

        public static void UpdateBox(FPhysicsShape2D shape, FPhysicsFixture2D fixture, FFloat width, FFloat height, FVector2 center, FFloat angle)
        {
            NativeFPPhysics2D.UpdateBox(shape.IntPointer, fixture.IntPointer, width, height, center, angle);
        }

        public static FPhysicsShape2D CreateCapsule(FVector2 v1, FVector2 v2, FFloat radius, FVector2 center, FFloat angle)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.CreateCapsule(v1, v2, radius, center, angle);
            return new FPhysicsShape2D(m_NativeObject);
        }

        public static void UpdateCapsule(FPhysicsShape2D shape, FPhysicsFixture2D fixture, FVector2 v1, FVector2 v2, FFloat radius, FVector2 center, FFloat angle)
        {
            NativeFPPhysics2D.UpdateCapsule(shape.IntPointer, fixture.IntPointer, v1, v2, radius, center, angle);
        }

        //polygon
        public static FPhysicsShape2D CreatePolygon(FVector2[] verts, int count, FVector2 center, FFloat angle)
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.CreatePolygon(verts, (uint)count, center, angle);
            return new FPhysicsShape2D(m_NativeObject);
        }

        public static void UpdatePolygon(FPhysicsShape2D shape, FPhysicsFixture2D fixture, FVector2[] verts, int count, FVector2 center, FFloat angle)
        {
            NativeFPPhysics2D.UpdatePolygon(shape.IntPointer, fixture.IntPointer, verts, (uint)count, center, angle);
        }

        // convex hull
        public static int ConvexHull2D(FVector2[] verts, int count, FVector2[] vertsOut, int limit)
        {
            UInt32 countOut = 0;

            NativeFPPhysics2D.ConvexHull2D(verts, (uint)count, vertsOut, ref countOut, limit);

            return (int)countOut;
        }

        //cast

        public static FRaycastHit2D RayCast(FVector2 p1, FVector2 p2)
        {
            return RayCast(p1, p2, -1);
        }

        public static FRaycastHit2D RayCast(FVector2 start, FVector2 end, int mask)
        {
            if (FVector2.Distance(start, end) < FPhysicsConstants.SMALLEST_RAYCAST_RANGE)
            {
                Debug.Log("RayCast range too short");
                return FRaycastHit2D.Null;
            }


            UInt16 bodyId = 0;
            byte fixtureId = 0;
            FVector2 point = FVector2.zero;
            FVector2 normal = FVector2.zero;
            FFloat fraction = FFloat.zero;

            bool hit = NativeFPPhysics2D.RayCast(start, end, mask, ref point, ref normal, ref fraction, ref bodyId, ref fixtureId, _world.IntPointer);

            FRaycastHit2D result = FRaycastHit2D.Null;

            if (hit)
            {
                result.point = point;
                result.normal = normal;
                result.fraction = fraction;

                if (_bodySortedList.ContainsKey(bodyId))
                {
                    result.rigidbody = _bodySortedList[bodyId].rigidBody;
                    FCollider2D collider = result.rigidbody.FindCollider(fixtureId);
                    result.collider = collider;
                    if (collider == null)
                    {
                        Debug.LogError($"Collider not found on Rigidbody({bodyId}): {fixtureId}");
                    }
                    else
                    {
                        result.transform = collider.fTransform;
                    }
                }
                else
                {
                    Debug.LogError($"Rigibody not found: {bodyId}");
                }
            }

            return result;
        }

        public static FRaycastHit2D CircleCast(FVector2 center, FFloat radius, FVector2 translation)
        {
            return CircleCast(center, radius, translation, -1);
        }

        public static FRaycastHit2D CircleCast(FVector2 center, FFloat radius, FVector2 translation, int mask)
        {
            UInt16 bodyId = 0;
            byte fixtureId = 0;
            FVector2 point = FVector2.zero;
            FVector2 normal = FVector2.zero;
            FFloat fraction = FFloat.zero;
            bool hit = NativeFPPhysics2D.CircleCast(center, radius, mask, translation, ref point, ref normal, ref fraction, ref bodyId, ref fixtureId, _world.IntPointer);

            FRaycastHit2D result = FRaycastHit2D.Null;

            if (hit)
            {
                result.point = point;
                result.normal = normal;
                result.fraction = fraction;

                if (_bodySortedList.ContainsKey(bodyId))
                {
                    result.rigidbody = _bodySortedList[bodyId].rigidBody;
                    FCollider2D collider = result.rigidbody.FindCollider(fixtureId);
                    result.collider = collider;
                    if(collider == null)
                    {
                        Debug.LogError($"Collider not found on Rigidbody({bodyId}): {fixtureId}");
                    }
                    else
                    {
                        result.transform = collider.fTransform;
                    }
                }
                else
                {
                    Debug.LogError($"Rigibody not found: {bodyId}");
                }
            }

            return result;
        }

        //overlap
        public static int OverlapCircle(FVector2 center, FFloat radius, FCollider2D[] colliders)
        {
            return OverlapCircle(center, radius, -1, colliders);
        }

        public static int OverlapCircle(FVector2 center, FFloat radius, int mask, FCollider2D[] colliders)
        {
            int count = 0;
            int limit = colliders.Length;
            if (limit > FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D)
            {
                limit = FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D;
            }

            bool hit = NativeFPPhysics2D.CircleOverlap(_world.IntPointer, center, radius, mask, _queryColliderIDs, limit, ref count);

            for (int i = 0; i < count; i++)
            {
                UInt16 bodyId = _queryColliderIDs[i].bodyId;
                byte fixtureId = _queryColliderIDs[i].fixtureId;

                if (_bodySortedList.ContainsKey(bodyId))
                {
                    FRigidbody2D rigidbody = _bodySortedList[bodyId].rigidBody;
                    FCollider2D collider = rigidbody.FindCollider(fixtureId);

                    if (collider == null)
                    {
                        Debug.LogError($"Collider not found on Rigidbody({bodyId}): {fixtureId}");
                    }
                    else
                    {
                        colliders[i] = collider;
                    }
                }
                else
                {
                    Debug.LogError($"Rigibody not found: {bodyId}");
                }
            }

            return count;
        }

        public static int OverlapBox(FVector2 center, FFloat width, FFloat height, FFloat angle, FCollider2D[] colliders)
        {
            return OverlapBox(center, width, height, angle, -1, colliders);
        }

        public static int OverlapBox(FVector2 center, FFloat width, FFloat height, FFloat angle, int mask, FCollider2D[] colliders)
        {
            int count = 0;
            int limit = colliders.Length;
            if(limit > FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D)
            {
                limit = FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D;
            }

            bool hit = NativeFPPhysics2D.BoxOverlap(_world.IntPointer, center, width, height, angle * FMath.Deg2Rad, mask, _queryColliderIDs, limit, ref count);

            for (int i = 0; i < count; i++)
            {
                UInt16 bodyId = _queryColliderIDs[i].bodyId;
                byte fixtureId = _queryColliderIDs[i].fixtureId;

                if (_bodySortedList.ContainsKey(bodyId))
                {
                    FRigidbody2D rigidbody = _bodySortedList[bodyId].rigidBody;
                    FCollider2D collider = rigidbody.FindCollider(fixtureId);

                    if (collider == null)
                    {
                        Debug.LogError($"Collider not found on Rigidbody({bodyId}): {fixtureId}");
                    }
                    else
                    {
                        colliders[i] = collider;
                    }
                }
                else
                {
                    Debug.LogError($"Rigibody not found: {bodyId}");
                }
            }

            return count;
        }

        public static int OverlapPolygon(FVector2[] verts, int vertsCount, FCollider2D[] colliders)
        {
            return OverlapPolygon(verts, vertsCount, -1, colliders);
        }

        public static int OverlapPolygon(FVector2[] verts, int vertsCount, int mask, FCollider2D[] colliders)
        {
            int count = 0;
            int limit = colliders.Length;
            if (limit > FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D)
            {
                limit = FPhysicsConstants.SHAPE_OVERLAP_COLLIDER_COUNT_2D;
            }

            bool hit = NativeFPPhysics2D.PolygonOverlap(_world.IntPointer, verts, (UInt32)vertsCount, mask, _queryColliderIDs, limit, ref count);

            for (int i = 0; i < count; i++)
            {
                UInt16 bodyId = _queryColliderIDs[i].bodyId;
                byte fixtureId = _queryColliderIDs[i].fixtureId;

                if (_bodySortedList.ContainsKey(bodyId))
                {
                    FRigidbody2D rigidbody = _bodySortedList[bodyId].rigidBody;
                    FCollider2D collider = rigidbody.FindCollider(fixtureId);

                    if (collider == null)
                    {
                        Debug.LogError($"Collider not found on Rigidbody({bodyId}): {fixtureId}");
                    }
                    else
                    {
                        colliders[i] = collider;
                    }
                }
                else
                {
                    Debug.LogError($"Rigibody not found: {bodyId}");
                }
            }

            return count;
        }

        //collision events
        internal static FCollision2D GetCollision(UInt64 collisionID, UInt16 bodyID)
        {
            Collision2DCache cache = _collisionCache[collisionID];

            FRigidbody2D rigidbody1 = _bodySortedList[cache.collider1.bodyId].rigidBody;
            FRigidbody2D rigidbody2 = _bodySortedList[cache.collider2.bodyId].rigidBody;

            FCollider2D collider1 = rigidbody1.FindCollider(cache.collider1.fixtureId);
            FCollider2D collider2 = rigidbody2.FindCollider(cache.collider2.fixtureId);

            if(bodyID == cache.collider1.bodyId)
            {
                return new FCollision2D { otherCollider = collider2, selfCollider = collider1 };
            }
            else
            {
                return new FCollision2D { otherCollider = collider1, selfCollider = collider2 };
            }
        }

        internal static IntPtr GetContactEdge(FPhysicsBody2D body)
        {
            return NativeFPPhysics2D.GetContactEdgeListOfBody(body.IntPointer);
        }

        internal static IntPtr GetContactInfoAndNextContactEdge(IntPtr edge, ref UInt64 contactId)
        {
            return NativeFPPhysics2D.GetContactInfoAndNextContactEdge(edge, ref contactId);
        }

        internal FCollider2D GetCollider(ColliderId2D colliderId2D)
        {
            FRigidbody2D rigidbody = _bodySortedList[colliderId2D.bodyId].rigidBody;
            FCollider2D collider = rigidbody.FindCollider(colliderId2D.fixtureId);

            return collider;
        }

        //rollback

        public static FSnapshot2D Snapshot()
        {
            IntPtr m_NativeObject = NativeFPPhysics2D.Snapshot(_world.IntPointer);
            return new FSnapshot2D(m_NativeObject);
        }

        public static void Restore(FSnapshot2D snapshot)
        {
            NativeFPPhysics2D.Restore(_world.IntPointer, snapshot.IntPointer);

            foreach (var pair in _bodySortedList)
            {
                FPhysicsBody2D body = pair.Value;
                ReadNativeBody(body);
                body.SyncTransform();
                body.SyncEnabled();
            }
        }

        public static void DestroySnapshot(FSnapshot2D snapshot)
        {
            NativeFPPhysics2D.DestroySnapshot(snapshot.IntPointer);
        }
    }
}